/*
 * Created on 2005-6-20
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.linkage.util.beanutils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ContextClassLoaderLocal;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaClass;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.PropertyUtilsBean;

import com.linkage.util.log.Logger;

/**
 * @author Administrator
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class BeanUtilsBeanEx extends BeanUtilsBean {

	/**
	 * 
	 */
	public BeanUtilsBeanEx() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param convertUtilsBean
	 * @param propertyUtilsBean
	 */
	public BeanUtilsBeanEx(ConvertUtilsBean convertUtilsBean,
			PropertyUtilsBean propertyUtilsBean) {
		super(convertUtilsBean, propertyUtilsBean);
		// TODO Auto-generated constructor stub
	}
	public void copyProperty(Object bean, String name, Object value)
    throws IllegalAccessException, InvocationTargetException {
//		if(value == null)
//			return;
		Logger log = Logger.getLogger(this.getClass());
		if (log.isInfoEnabled()) {
            StringBuffer sb = new StringBuffer("  copyProperty(");
            sb.append(bean);
            sb.append(", ");
            sb.append(name);
            sb.append(", '");
            if (value == null) {
                sb.append("<NULL>");
            } else if (value instanceof String) {
                sb.append((String) value);
            } else if (value instanceof String[]) {
                String values[] = (String[]) value;
                sb.append('[');
                for (int i = 0; i < values.length; i++) {
                    if (i > 0) {
                        sb.append(',');
                    }
                    sb.append(values[i]);
                }
                sb.append(']');
            } else {
                sb.append(value.toString());
            }
            sb.append("')");
            log.info(sb.toString());
        }

        // Resolve any nested expression to get the actual target bean
        Object target = bean;
        int delim = name.lastIndexOf(PropertyUtils.NESTED_DELIM);
        if (delim >= 0) {
            try {
                target =
                    getPropertyUtils().getProperty(bean, name.substring(0, delim));
            } catch (NoSuchMethodException e) {
                return; // Skip this property setter
            }
            name = name.substring(delim + 1);
//            if (log.isTraceEnabled()) {
//                log.trace("    Target bean = " + target);
//                log.trace("    Target name = " + name);
//            }
        }

        // Declare local variables we will require
        String propName = null;          // Simple name of target property
        Class<?> type = null;               // Java type of target property
        int index = -1;                  // Indexed subscript value (if any)
        String key = null;               // Mapped key value (if any)

        // Calculate the target property name, index, and key values
        propName = name;
        int i = propName.indexOf(PropertyUtils.INDEXED_DELIM);
        if (i >= 0) {
            int k = propName.indexOf(PropertyUtils.INDEXED_DELIM2);
            try {
                index =
                    Integer.parseInt(propName.substring(i + 1, k));
            } catch (NumberFormatException e) {
                ;
            }
            propName = propName.substring(0, i);
        }
        int j = propName.indexOf(PropertyUtils.MAPPED_DELIM);
        if (j >= 0) {
            int k = propName.indexOf(PropertyUtils.MAPPED_DELIM2);
            try {
                key = propName.substring(j + 1, k);
            } catch (IndexOutOfBoundsException e) {
                ;
            }
            propName = propName.substring(0, j);
        }

        // Calculate the target property type
        if (target instanceof DynaBean) {
            DynaClass dynaClass = ((DynaBean) target).getDynaClass();
            DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);
            if (dynaProperty == null) {
                return; // Skip this property setter
            }
            type = dynaProperty.getType();
        } else {
            PropertyDescriptor descriptor = null;
            try {
                descriptor =
                    getPropertyUtils().getPropertyDescriptor(target, name);
                if (descriptor == null) {
                    return; // Skip this property setter
                }
            } catch (NoSuchMethodException e) {
                return; // Skip this property setter
            }
            type = descriptor.getPropertyType();
            if (type == null) {
                // Most likely an indexed setter on a POJB only
//                if (log.isTraceEnabled()) {
//                    log.trace("    target type for property '" +
//                              propName + "' is null, so skipping ths setter");
//                }
                return;
            }
        }
//        if (log.isTraceEnabled()) {
//            log.trace("    target propName=" + propName + ", type=" +
//                      type + ", index=" + index + ", key=" + key);
//        }

        // Convert the specified value to the required type and store it
        if (index >= 0) {                    // Destination must be indexed
            Converter converter = getConvertUtils().lookup(type.getComponentType());
            if (converter != null) {
//                log.trace("        USING CONVERTER " + converter);
                value = converter.convert(type, value);
            }
            try {
                getPropertyUtils().setIndexedProperty(target, propName,
                                                 index, value);
            } catch (NoSuchMethodException e) {
                throw new InvocationTargetException
                    (e, "Cannot set " + propName);
            }
        } else if (key != null) {            // Destination must be mapped
            // Maps do not know what the preferred data type is,
            // so perform no conversions at all
            // FIXME - should we create or support a TypedMap?
            try {
                getPropertyUtils().setMappedProperty(target, propName,
                                                key, value);
            } catch (NoSuchMethodException e) {
                throw new InvocationTargetException
                    (e, "Cannot set " + propName);
            }
        } else {                             // Destination must be simple
        	if(value == null){
        		try {
                    getPropertyUtils().setSimpleProperty(target, propName, null);
                } catch (NoSuchMethodException e) {
                    throw new InvocationTargetException
                        (e, "Cannot set " + propName);
                }
        	}else{
        		Converter converter = null;
				if (type == java.util.Date.class)
					converter = new UtilDateConverter();
				else
					converter = getConvertUtils().lookup(type);
				if (converter != null) {
					// log.trace(" USING CONVERTER " + converter);
					value = converter.convert(type, value);
				}
				if (type == java.lang.String.class
						&& (((String) value).equals("") || ((String) value)
								.equals(" ")))
					value = null;
				try {
					getPropertyUtils().setSimpleProperty(target, propName,
							value);
				} catch (NoSuchMethodException e) {
					throw new InvocationTargetException(e, "Cannot set "
							+ propName);
				}
        	}
        }
	}
	/**
	 * Contains <code>BeanUtilsBean</code> instances indexed by context
	 * classloader.
	 */
    private static final ContextClassLoaderLocal 
            beansByClassLoader = new ContextClassLoaderLocal() {
                        // Creates the default instance used when the context classloader is unavailable
                        protected Object initialValue() {
                            return new BeanUtilsBeanEx();
                        }
                    };
    
    /** 
     * Gets the instance which provides the functionality for {@link BeanUtils}.
     * This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
     * This mechanism provides isolation for web apps deployed in the same container. 
     */
    public synchronized static BeanUtilsBeanEx getInstanceEx() {
        return (BeanUtilsBeanEx) beansByClassLoader.get();
    }
}
